local frame
local OptionHouse

local TOTAL_ROWS = 15
local L = {
	["MEMORY"] = "Memory",
	["MEMSEC"] = "Memory/Sec",
	["CPU"] = "CPU",
	["CPUSEC"] = "CPU/Sec",
	["ENABLE_CPU"] = "Enable CPU",
	["DISABLE_CPU"] = "Disable CPU",
	["RELOAD_UI"] = "Reload UI",
	["ADDON_PERFORMANCE"] = "Performance",
	["NAME"] = "Name",
}

local function sortPerformanceList(a, b)
	if( not b ) then
		return false
	end

	if( frame.sortOrder ) then
		if( frame.sortType == "name" ) then
			return ( a.title < b.title )
		elseif( frame.sortType == "memory" ) then
			return ( a.memory < b.memory )
		elseif( frame.sortType == "cpu" ) then
			return ( a.cpu < b.cpu )
		elseif( frame.sortType == "mir" ) then
			-- If mir is 0 for both, sort by name
			-- this prevents everything from moving around randomly
			-- and generally just looking ugly
			if( a.mir == 0 and b.mir == 0 ) then
				return ( a.title < b.title )
			end

			return ( a.mir < b.mir )

		elseif( frame.sortType == "cir" ) then
			if( a.cir == 0 and b.cir == 0 ) then
				return ( a.title < b.title )
			end

			return ( a.cir < b.cir )
		end

		return ( a.memory < b.memory )

	else
		if( frame.sortType == "name" ) then
			return ( a.title > b.title )
		elseif( frame.sortType == "memory" ) then
			return ( a.memory > b.memory )
		elseif( frame.sortType == "cpu" ) then
			return ( a.cpu > b.cpu )
		elseif( frame.sortType == "mir" ) then
			if( a.mir == 0 and b.mir == 0 ) then
				return ( a.title > b.title )
			end

			return ( a.mir > b.mir )

		elseif( frame.sortType == "cir" ) then
			if( a.cir == 0 and b.cir == 0 ) then
				return ( a.title > b.title )
			end

			return ( a.cir > b.cir )
		end

		return ( a.memory > b.memory )
	end
end

local function updateAddonPerformance()
	UpdateAddOnMemoryUsage()
	UpdateAddOnCPUUsage()

	local totalMemory = 0
	local totalCPU = 0

	for id, addon in pairs(frame.addons) do
		memory = GetAddOnMemoryUsage(addon.name)
		cpu = GetAddOnCPUUsage(addon.name)

		totalMemory = totalMemory + memory
		totalCPU = totalCPU + cpu

		frame.addons[id].mir = memory - addon.memory
		frame.addons[id].cir = cpu - addon.cpu
		frame.addons[id].memory = memory
		frame.addons[id].cpu = cpu
	end

	for id, addon in pairs(frame.addons) do
		frame.addons[id].cpuPerct = frame.addons[id].cpu / totalCPU * 100
		frame.addons[id].memPerct = frame.addons[id].memory / totalMemory * 100
	end
end

local function updatePerformanceList()
	if( not frame:IsShown() ) then
		return
	end

	table.sort(frame.addons, sortPerformanceList)

	FauxScrollFrame_Update(frame.scroll, #(frame.addons), TOTAL_ROWS, 20)
	local offset = FauxScrollFrame_GetOffset(frame.scroll)

	for i=1, TOTAL_ROWS do
		local index = offset + i
		local column1, column2, column3, column4, column5 = unpack(frame.columns[i])

		if( index <= #(frame.addons) ) then
			local addon = frame.addons[index]
			column1:SetText(addon.title)

			if( addon.memory > 1000 ) then
				column2:SetText(string.format("%.3f MiB (%.2f%%)", addon.memory / 1000, addon.memPerct))
			else
				column2:SetText(string.format("%.3f KiB (%.2f%%)", addon.memory, addon.memPerct))
			end

			if( addon.mir > 1000 ) then
				column3:SetText(string.format("%.3f MiB/s", addon.mir / 1000))
			else
				column3:SetText(string.format("%.3f KiB/s", addon.mir))
			end

			if( scriptProfiling ) then
				column4:SetText(string.format("%.3f (%.2f%%)", addon.cpu, addon.cpuPerct))
				column5:SetText(string.format("%.3f", addon.cir))
			else
				column4:SetText("----")
				column5:SetText("----")
			end

			column1:Show()
			column2:Show()
			column3:Show()
			column4:Show()
			column5:Show()
		else
			column1:Hide()
			column2:Hide()
			column3:Hide()
			column4:Hide()
			column5:Hide()
		end
	end
end

local elapsed = 0
local function performanceOnUpdate(self, time)
	elapsed = elapsed + time

	if( elapsed >= 1 ) then
		elapsed = 0

		updateAddonPerformance()
		updatePerformanceList()
	end
end

local function sortPerfClick(self)
	if( self.sortType ) then
		if( self.sortType ~= frame.sortType ) then
			frame.sortOrder = false
			frame.sortType = self.sortType
		else
			frame.sortOrder = not frame.sortOrder
		end

		updatePerformanceList()
	end
end

-- Create a list now so we aren't creating a new table/list every OnUpdate
local function updateAddonPerfList()
	if( frame:IsShown() ) then
		local searchBy = string.trim(string.lower(OptionHousePerformanceFrameSearch:GetText()))
		if( searchBy == "" or OptionHousePerformanceFrameSearch.searchText ) then
			searchBy = nil
		end

		frame.addons = {}
		for i=1, GetNumAddOns() do
			local name, title = GetAddOnInfo(i)
			if( IsAddOnLoaded(i) and ( ( searchBy and string.find(string.lower(name), searchBy)) or not searchBy ) ) then
				table.insert(frame.addons, {name = name, title = title, mir = 0, cir = 0, cpu = 0, memory = 0})
			end
		end
	end
end

local function addonPerfTab(hide)
	local name = "OptionHousePerformanceFrame"
	frame = getglobal(name)

	if( frame and hide ) then
		frame:Hide()
		return
	elseif( hide ) then
		return
	elseif( not frame ) then
		frame = CreateFrame("Frame", name, OptionHouseFrame)
		frame:SetFrameStrata("MEDIUM")
		frame:SetAllPoints(OptionHouseFrame)
		frame.sortOrder = nil
		frame.sortType = "name"

		local toggleCPU = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
		toggleCPU:SetWidth(80)
		toggleCPU:SetHeight(22)
		toggleCPU:SetPoint("BOTTOMRIGHT", OptionHouseFrame, "BOTTOMRIGHT", -8, 14)
		toggleCPU:SetScript("OnClick", function(self)
			if( GetCVar("scriptProfile") == "1" ) then
				self:SetText(L["ENABLE_CPU"])
				SetCVar("scriptProfile", "0", 1)
			else
				self:SetText(L["DISABLE_CPU"])
				SetCVar("scriptProfile", "1", 1)
			end
		end)

		-- UI Reload required for CPU profiling to be usable, so check on load
		if( GetCVar("scriptProfile") == "1" ) then
			scriptProfiling = true
			toggleCPU:SetText(L["DISABLE_CPU"])
		else
			toggleCPU:SetText(L["ENABLE_CPU"])
		end

		local reloadUI = CreateFrame("Button", nil, frame, "UIPanelButtonGrayTemplate")
		reloadUI:SetWidth(80)
		reloadUI:SetHeight(22)
		reloadUI:SetPoint("RIGHT", toggleCPU, "LEFT")
		reloadUI:SetText(L["RELOAD_UI"])
		reloadUI:SetScript("OnClick", ReloadUI)

		local button
		for i=1, 5 do
			button = CreateFrame("Button", name .. "SortButton" .. i, frame)
			button:SetScript("OnClick", sortPerfClick)
			button:SetHeight(20)
			button:SetWidth(75)
			button:SetTextFontObject(GameFontNormal)
		end

		local button = OptionHousePerformanceFrameSortButton1
		button.sortType = "name"
		button:SetText(L["NAME"])
		button:SetWidth(button:GetFontString():GetStringWidth() + 3)
		button:SetPoint("TOPLEFT", frame, "TOPLEFT", 25, -75)
		button:Show()

		button = OptionHousePerformanceFrameSortButton2
		button.sortType = "memory"
		button:SetText(L["MEMORY"])
		button:SetWidth(button:GetFontString():GetStringWidth() + 3)
		button:SetPoint("TOPLEFT", OptionHousePerformanceFrameSortButton1, "TOPLEFT", 220, 0)
		button:Show()

		button = OptionHousePerformanceFrameSortButton3
		button.sortType = "mir"
		button:SetText(L["MEMSEC"])
		button:SetWidth(button:GetFontString():GetStringWidth() + 3)
		button:SetPoint("TOPLEFT", OptionHousePerformanceFrameSortButton2, "TOPLEFT", 170, 0)
		button:Show()

		button = OptionHousePerformanceFrameSortButton4
		button.sortType = "cpu"
		button:SetText(L["CPU"])
		button:SetWidth(button:GetFontString():GetStringWidth() + 3)
		button:SetPoint("TOPLEFT", OptionHousePerformanceFrameSortButton3, "TOPLEFT", 140, 0)
		button:Show()

		button = OptionHousePerformanceFrameSortButton5
		button.sortType = "cir"
		button:SetText(L["CPUSEC"])
		button:SetWidth(button:GetFontString():GetStringWidth() + 3)
		button:SetPoint("TOPLEFT", OptionHousePerformanceFrameSortButton4, "TOPLEFT", 150, 0)
		button:Show()

		local path, size, border = GameFontNormalSmall:GetFont()
		size = OptionHouseDB.perfFontSize
		
		-- Setup a fake font string so we can figure out the fonts height
		local test = frame:CreateFontString()
		test:SetFont(path, size, border)
		test:SetText("*")
		
		-- Now figure out padding inbetween each row
		local spacing = -10 - test:GetHeight()		
		
		-- And now figure out how many rows we can support
		TOTAL_ROWS = floor(305 / abs(spacing))

		frame.columns = {}
		for i=1, TOTAL_ROWS do
			frame.columns[i] = {}
			for j=1, 5 do
				text = frame:CreateFontString(name .. "SortRow" .. i .. "Column" .. j, frame)
				text:SetFont(path, size, border)
				text:SetTextColor(1, 1, 1)
				text:Hide()
				frame.columns[i][j] = text

				if( i > 1 ) then
					text:SetPoint("TOPLEFT", name .. "SortRow" .. i - 1 .. "Column" .. j, "TOPLEFT", 0, spacing)
				else
					text:SetPoint("TOPLEFT", name .. "SortButton" .. j, "TOPLEFT", 2, -28)
				end
			end
		end

		frame.scroll = CreateFrame("ScrollFrame", name.."Scroll", frame, "FauxScrollFrameTemplate")
		frame.scroll:SetPoint("TOPLEFT", frame, "TOPLEFT", 25, -76)
		frame.scroll:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -35, 38)
		frame.scroll:SetScript("OnVerticalScroll", function()
			FauxScrollFrame_OnVerticalScroll(22, updatePerformanceList)
		end)

		local texture = frame.scroll:CreateTexture(nil, "BACKGROUND")
		texture:SetWidth(31)
		texture:SetHeight(256)
		texture:SetPoint("TOPLEFT", frame.scroll, "TOPRIGHT", -2, 5)
		texture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar")
		texture:SetTexCoord(0, 0.484375, 0, 1.0)

		local texture = frame.scroll:CreateTexture(nil, "BACKGROUND")
		texture:SetWidth(31)
		texture:SetHeight(106)
		texture:SetPoint("BOTTOMLEFT", frame.scroll, "BOTTOMRIGHT", -2, -2)
		texture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar")
		texture:SetTexCoord(0.515625, 1.0, 0, 0.4140625)

		frame:SetScript("OnUpdate", performanceOnUpdate)
		frame:SetScript("OnEvent", updatePerformanceList)
		frame:RegisterEvent("ADDON_LOADED")

		OptionHouse:CreateSearchInput(frame, function()
			updateAddonPerfList()
			updateAddonPerformance()
			updatePerformanceList()
		end)
	end

	frame:Show()

	updateAddonPerfList()
	updateAddonPerformance()
	updatePerformanceList()
end

-- For configuraiton
OHPerformance = {}

function OHPerformance:Register()
	if( not OptionHouse ) then
		OptionHouse = DongleStub("OptionHouse-1.0")
	end

	OptionHouse:RegisterTab(L["ADDON_PERFORMANCE"], addonPerfTab, "bid")
end

function OHPerformance:Reload()

end

function OHPerformance:Unregister()
	if( OptionHouse ) then
		OptionHouse:UnregisterTab(L["ADDON_PERFORMANCE"])
	end
end